package agents.auctioneeragentbehaviours;

import java.io.IOException;
import java.util.ArrayList;

import resource.Artifact;

import jade.core.AID;
import jade.core.behaviours.FSMBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.SimpleBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.domain.FIPANames.InteractionProtocol;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.UnreadableException;

public class AuctionBehaviour extends FSMBehaviour{
	private static final String STATE_A = "A";
	private static final String STATE_B = "B";
	private static final String STATE_C = "C";	
	private static final String STATE_D = "D";
	private static final String STATE_E = "E";
	private static final String STATE_F = "F";
	private ArrayList<AID> participants;
	private Artifact artifactToSell;
	private float lowestPrice;
	private float currentBid;
	private int numberOfCFP = 0;
	private boolean sold = false;
	private float price = 0;
	
	public AuctionBehaviour(ArrayList<AID> participants, Artifact artifactToSell, float lowestPrice){
		this.participants = participants;
		this.artifactToSell = artifactToSell;
		this.lowestPrice = lowestPrice;
		this.currentBid = lowestPrice + lowestPrice/2;
		
		//this.registerFirstState(new GetAuctionParticipants(), STATE_A);
		this.registerFirstState(new SendInformStartOfAuctionBehaviour(), STATE_B);
		this.registerState(new SendCFPBehaviour(), STATE_C);
		this.registerState(new ListenForProposalsBehaviour(), STATE_D);
		this.registerState(new InformAuctionEndIfSoldOrLowestPriceIsNotAcceptedBehaviour(), STATE_E);
		this.registerLastState(new OneShotBehaviour() {
			
			@Override
			public void action() {
				System.out.println("Done");
			}
		}, STATE_F);
		
		//this.registerDefaultTransition(STATE_A, STATE_B);
		this.registerDefaultTransition(STATE_B, STATE_C);
		this.registerDefaultTransition(STATE_C, STATE_D);
		this.registerDefaultTransition(STATE_D, STATE_E);
		this.registerDefaultTransition(STATE_E, STATE_F);
		
		this.registerTransition(STATE_E,STATE_C,1);
	}
	
	/*private class GetAuctionParticipants extends OneShotBehaviour{

		@Override
		public void action() {
			System.out.println("Getting auction participants");
			DFAgentDescription dfd = new DFAgentDescription();
	        ServiceDescription sd  = new ServiceDescription();
	        sd.setType("auctionParticipant");
	        dfd.addServices(sd);
	        
	        DFAgentDescription[] result = null;
			try{
				result = DFService.search(myAgent, dfd);
			}catch (FIPAException e){
				throw new RuntimeException(e);
			}
			
			if(result.length > 0){
				System.out.println(result.length + " auction participants found");
				participants = new AID[result.length];
				for(int i = 0; i < participants.length; i++)
					participants[i] = result[i].getName();
			}
		}
	}*/
	
	private class SendInformStartOfAuctionBehaviour extends OneShotBehaviour{

		@Override
		public void action() {
			for(int i = 0; i < participants.size(); i++){
				System.out.println("Sending INFORM message to " + participants.get(i).getLocalName());
				ACLMessage message = new ACLMessage(ACLMessage.INFORM);
				message.setProtocol(InteractionProtocol.FIPA_DUTCH_AUCTION);
				message.addReceiver(participants.get(i));
				message.setSender(myAgent.getAID());
				try {
					message.setContentObject(artifactToSell);
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
				myAgent.send(message);
			}
		}
	}
	
	private class SendCFPBehaviour extends OneShotBehaviour{

		@Override
		public void action() {
			numberOfCFP++;
			for(int i = 0; i < participants.size(); i++){
				System.out.println(myAgent.getLocalName() + ": Sending CFP message to " + participants.get(i).getLocalName());
				ACLMessage message = new ACLMessage(ACLMessage.CFP);
				message.setProtocol(InteractionProtocol.FIPA_DUTCH_AUCTION);
				message.addReceiver(participants.get(i));
				message.setSender(myAgent.getAID());
				try {
					message.setContentObject(new Float(currentBid));
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
				myAgent.send(message);
			}
		}
	}
	
	private class ListenForProposalsBehaviour extends SimpleBehaviour{
		private int numberOfParticipants;
		private int numberOfMessagesReceived = 0;
		
		@Override
		public void action() {
			System.out.println(myAgent.getLocalName() + ": Listening for proposals");
			numberOfParticipants = participants.size();
			ACLMessage msg = myAgent.receive();
			if(msg != null){
				if(msg.getProtocol().equals(InteractionProtocol.FIPA_DUTCH_AUCTION) && msg.getPerformative() == ACLMessage.PROPOSE){
					System.out.println(myAgent.getLocalName() + ": Message received from " + msg.getSender().getLocalName());
					numberOfMessagesReceived++;
					ACLMessage messageToReceiver = msg.createReply();
					if(msg.getPerformative() == ACLMessage.PROPOSE){
						try {
							if((Boolean) msg.getContentObject()){
								if(!sold){
									System.out.println(myAgent.getLocalName() + ": Sold to " + msg.getSender().getLocalName() + "for " + currentBid);
									sold = true;
									price = currentBid;
									messageToReceiver.setPerformative(ACLMessage.ACCEPT_PROPOSAL);
								}
								else{
									System.out.println(myAgent.getLocalName() + ": Bid rejected by " + msg.getSender().getLocalName());
									messageToReceiver.setPerformative(ACLMessage.REJECT_PROPOSAL);
								}
							}
							else{
								messageToReceiver.setPerformative(ACLMessage.ACCEPT_PROPOSAL);
							}
						} catch (UnreadableException e) {
							throw new RuntimeException(e);
						}
						myAgent.send(messageToReceiver);
					}
					else if(msg.getPerformative() == ACLMessage.NOT_UNDERSTOOD){
						System.out.println(myAgent.getLocalName() + ": NOT_UNDERSTOOD message received from " 
																	+ msg.getSender().getLocalName());
					}
				}
				else{
					System.out.println(myAgent.getLocalName() + ": Unknown protocol received from " + msg.getSender().getLocalName());
					block();
				}
			}
			else
				block();
		}

		@Override
		public boolean done() {
			if(numberOfMessagesReceived < numberOfParticipants)
				return false;
			else{
				numberOfMessagesReceived = 0;
				System.out.println(myAgent.getLocalName() + ": Proposal received from all participants");
				return true;
			}
		}
		
		@Override
		public void reset() {
			numberOfParticipants = participants.size();
			super.reset();
		}
	}
	
	class InformAuctionEndIfSoldOrLowestPriceIsNotAcceptedBehaviour extends OneShotBehaviour{
		int onEnd;
		@Override
		public void action() {
			currentBid -= 200;
			if(sold || currentBid < lowestPrice){
				if(currentBid < lowestPrice && !sold)
					System.out.println(myAgent.getLocalName() + ": Price is to low: "+ currentBid + "<" + lowestPrice);
				
				for(int i = 0; i < participants.size(); i++){
					System.out.println(myAgent.getLocalName() + ": Sending CANCEL message to " + participants.get(i).getLocalName());
					ACLMessage message = new ACLMessage(ACLMessage.CANCEL);
					message.setProtocol(InteractionProtocol.FIPA_DUTCH_AUCTION);
					message.addReceiver(participants.get(i));
					message.setSender(myAgent.getAID());
					try {
						message.setContentObject(artifactToSell);
					} catch (IOException e) {
						throw new RuntimeException(e);
					}
					myAgent.send(message);
				}
				onEnd = 0;
			}
			else{
				System.out.println(myAgent.getLocalName() + ": Sending new CFP with bid " + currentBid);
				onEnd = 1;
			}
		}
		
		@Override
		public int onEnd() {
			return onEnd;
		}
	}

	public float getPrice() {
		return price;
	}
}
